Skip to content

feat(relay): add local single-node message tracer - #5580

Open
loganj wants to merge 9 commits into
brother-darryl/local-mode-pr1-communityfrom
brother-darryl/local-mode-pr2-messages
Open

feat(relay): add local single-node message tracer#5580
loganj wants to merge 9 commits into
brother-darryl/local-mode-pr1-communityfrom
brother-darryl/local-mode-pr2-messages

Conversation

@loganj

@loganj loganj commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

🤖

Summary

  • Add the local single-node SQLite message tracer: persist and replay relay events while retaining the authenticated send/read demo path.
  • Preserve the PR1 desktop-sidecar foundations: membership authorization, owner-aware community rebind, and loopback-only metrics and health listeners.
  • Add SQLite relay-handler conformance coverage and make the local smoke provision its channel before subscribing.

Scope

This PR is intentionally message-tracer only. DM/FTS, reactions, and feed work are deferred to PR3+.

Provenance and stack notes

  • Carries the message-tracer work from source 7db512e48 and cherry-picks the SQLite relay-handler conformance gate as 1a7883c4f, from wip-experiment-local-mode at b3f365faf.
  • The smoke provision-own-channel adjustment and pulling the conformance gate ahead of its source-stack position are new work in this PR.
  • Reconciles the conformance gate's fixture dependency from source 46283f403; two documented SQLite skip: annotations defer feedback-admin reads until PR5. The mesh-demo timeout remains a known parallel-suite environmental flake and passes in isolation.
  • This is stacked on brother-darryl/local-mode-pr1-community; expect a small rebase when PR1's Windows fix lands.

Validation note

RUST_TEST_THREADS=1 cargo test -p buzz-relay --lib has the same pre-existing/environmental failures at base and head: two admin 500 vs expected 404 failures, six media Sqlx(PoolTimedOut) failures, and an intermittent mesh-demo timeout. There are no head-only failures.

npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je and others added 9 commits August 11, 2026 11:03
Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
The PR1 base removed local_mode.rs. Keep its removal while the PR2
single-node profile supplies the supported local runtime.

Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
(cherry picked from commit 045b9c0)
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Centralize relay test state construction behind BUZZ_TEST_BACKEND, defaulting to the unchanged PostgreSQL path and providing a migrated in-memory SQLite database with a configured community fixture.

Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz>
@loganj
loganj marked this pull request as ready for review August 12, 2026 14:48
@loganj
loganj requested a review from a team as a code owner August 12, 2026 14:48

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80c2fe17ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/buzz-db/src/lib.rs
}
}
RouteDecision::Writer => event::count_events(&self.pool, q).await,
RouteDecision::Writer => event::count_events(self.pg_pool()?, q).await,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Route SQLite COUNT through the embedded backend

In the single-node profile, every fully pushable WebSocket or HTTP COUNT reaches count_events_routed; without a replica, this writer arm calls pg_pool(), which returns UnsupportedBackend for a SQLite Db. Consequently ordinary NIP-45 COUNT requests are closed with an error and POST /count returns an internal error instead of a count. Add a SQLite counting path, analogous to the new SQLite branches in query_events_routed.

AGENTS.md reference: AGENTS.md:L123-L135

Useful? React with 👍 / 👎.

Comment thread crates/buzz-db/src/lib.rs
Comment on lines +2214 to +2218
if let DbBackend::SQLite(pool) = &self.backend {
// SQLite Phase 1 persists the canonical event; thread relationships
// remain durable in the event's NIP-10 tags until Phase 2 ports the
// denormalized thread metadata tables.
let _ = thread_meta;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Persist SQLite thread metadata when inserting replies

Whenever ingest derives thread_meta for a NIP-10 reply, the SQLite branch explicitly discards it and stores only the event. Local replies therefore never update the root's materialized reply_count or descendant_count, and thread metadata reads cannot reconstruct the relationship. The SQLite insert must persist the metadata and update the counters atomically rather than dropping this argument.

AGENTS.md reference: AGENTS.md:L160-L162

Useful? React with 👍 / 👎.

Comment on lines +404 to +405
if event.created_at < existing.created_at
|| (event.created_at == existing.created_at && event.id <= existing.id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the lowest ID on equal-timestamp replacements

When two replaceable events have the same second-resolution created_at, NIP-16 ordering and the PostgreSQL implementation keep the lexicographically lowest event ID. This comparison does the reverse: it rejects an incoming lower ID and allows a higher ID to delete the existing event. Same-second profile, metadata, or other replaceable writes can therefore converge to different heads depending on whether the relay uses SQLite or PostgreSQL.

Useful? React with 👍 / 👎.

d_tag: Option<&str>,
) -> Result<(buzz_core::StoredEvent, bool)> {
let mut tx = pool.begin().await?;
let rows = sqlx::query("SELECT id, event_json FROM events WHERE community_id = ?1 AND kind = ?2 AND pubkey = ?3 AND channel_id IS ?4")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore channels when replacing NIP-33 coordinates

For d_tag.is_some(), the replacement coordinate is (kind, pubkey, d_tag) and intentionally does not include channel_id, as documented by replace_parameterized_event. This query nevertheless restricts candidates to the incoming channel. If an author publishes a newer version of the same coordinate in another channel, SQLite leaves the old version live and inserts a second head, so later REQs can return both versions.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant